﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Utility;

namespace SynAnalysis
{
    /// <summary>
    /// 语法分析
    /// </summary>
    public class SynAnalysis:ISynAnalysis.ISynAnalysis
    {
        //是否通过语法分析
        private bool _success;
        //多维单词表
        private readonly TokenTable _tokenTable;
        //词法分析程序实例
        private readonly ILexAnalysis.ILexAnalysis _lex;
        //当前单词符号
        private string _token;
        //指令计数序号
        private int _count;
        //临时变量序号
        private int _temp;
        //是否生成库文件
        private readonly bool _isForLib;

        //TODO 函数不能嵌套定义的处理

        /// <summary>
        /// 初始化
        /// </summary>
        public SynAnalysis(string code,bool isForLib)
        {
            _success = true;
            _tokenTable = new TokenTable();
            _lex = Factory.Factory.GetLex(code);
            _token = "";
            _count = 0;
            _temp = 0;
            _isForLib = isForLib;
            Program();
        }

        /// <summary>
        /// 获取多维单词表
        /// </summary>
        /// <returns></returns>
        public TokenTable GetTokenTable()
        {
            return _tokenTable;
        }

        /// <summary>
        /// 是否成功通过语法分析
        /// </summary>
        /// <returns></returns>
        public bool Success()
        {
            return _success;
        }

        //程序体
        private void Program()
        {
            _token = _lex.GetNextToken();
            while (_lex.GetSym() == 99) //换行
            {
                _token = _lex.GetNextToken();
            }
            if (_lex.GetSym() == 0) //USING
            {
                IncludeFilesPart();
            }
            if(_isForLib)
            {
                LibFile();
            }
            else
            {
                Recipe();
            }
            //switch (_lex.GetSym())
            //{
            //    case 1: //NEED
            //        Recipe();
            //        break;
            //    case 9: //FUNCTION
            //        LibFile();
            //        break;
            //    default:
            //        _success = false;
            //        Console.Error.WriteLine("Error:Should be Ingredients or Functions... Line:{0}", _lex.GetLine());
            //        break;
            //}
            if (_lex.GetSym() != -2) //文件结尾
            {
                _success = false;
                Console.Error.WriteLine("Error:Should be the End of file... Line:{0}", _lex.GetLine());
            }
        }

        //库文件包含部分
        private void IncludeFilesPart()
        {
            while (_lex.GetSym() == 0)//USING
            {
                IncludeFiles();
            }
        }

        //包含命令
        private void IncludeFiles()
        {
            _token = _lex.GetNextToken();
            if(_lex.GetSym() == 22)//文件名
            {
                _tokenTable.IncludeFiles.Add(_token);
                _token = _lex.GetNextToken();
                if (_lex.GetSym() == 99) //换行
                {
                    _token = _lex.GetNextToken();
                    while (_lex.GetSym() == 99) //换行
                    {
                        _token = _lex.GetNextToken();
                    }
                }
                else
                {
                    _success = false;
                    Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
                }
            }
            else
            {
                _success = false;
                Console.Error.WriteLine("Error:Should be Correct filename... Line:{0}", _lex.GetLine());
            }
        }

        //菜谱
        private void Recipe()
        {
            if (_lex.GetSym() == 1)//NEED
            {
                IngredientsPart();
                if (_lex.GetSym() == 9) //FUNCTION
                {
                    FunctionsPart();
                }
                MainPart();
            }
            else
            {
                _success = false;
                Console.Error.WriteLine("Error:Should be Ingredients... Line:{0}", _lex.GetLine());
            }
        }

        //库文件
        private void LibFile()
        {
            if (_lex.GetSym() == 9)//FUNCTION
            {
                FunctionsPart();
            }
            else
            {
                _success = false;
                Console.Error.WriteLine("Error:Should be Functions... Line:{0}", _lex.GetLine());
            }
        }

        //食材说明部分
        private void IngredientsPart()
        {
            while (_lex.GetSym() == 1) //NEED
            {
                Ingredients();
            }
        }

        //食材说明命令
        private void Ingredients()
        {
            _token = _lex.GetNextToken();
            if(_lex.GetSym()==21)//数值
            {
                double amount = double.Parse(_token);
                _token = _lex.GetNextToken();
                if (_lex.GetSym() == 20 && (_token == "G" || _token == "ML"))//标识符
                {
                    Dimension dimension = _token == "G" ? Dimension.g : Dimension.ml;
                    _token = _lex.GetNextToken();
                    if(_lex.GetSym()==20)//标识符
                    {
                        string name = _token;
                        _tokenTable.Ingredients.Add(new Ingredient(name,amount,dimension));
                        _token = _lex.GetNextToken();
                        if (_lex.GetSym() == 99) //换行
                        {
                            _token = _lex.GetNextToken();
                            while (_lex.GetSym() == 99) //换行
                            {
                                _token = _lex.GetNextToken();
                            }
                        }
                        else
                        {
                            _success = false;
                            Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
                        }
                    }
                    else
                    {
                        _success = false;
                        Console.Error.WriteLine("Error:Should be Correct Identifier... Line:{0}",_lex.GetLine());
                    }
                }
                else
                {
                    _success = false;
                    Console.Error.WriteLine("Error:Should be \"g\" or \"ml\"... Line:{0}",_lex.GetLine());
                }
            }
            else
            {
                _success = false;
                Console.Error.WriteLine("Error:Should be a Number... Line:{0}",_lex.GetLine());
            }
        }

        //自定义函数部分
        private void FunctionsPart()
        {
            while (_lex.GetSym() == 9) //FUNCTION
            {
                CookingFunction func = Function();
                if (func != null)
                {
                    _tokenTable.CookingFunctions.Add(func);
                }
            }
        }

        //自定义函数命令
        private CookingFunction Function()
        {
            _token = _lex.GetNextToken();
            if (_lex.GetSym() == 20) //标识符
            {
                CookingFunction func = new CookingFunction(_token);
                _token = _lex.GetNextToken();
                while (_lex.GetSym() == 20) //标识符
                {
                    func.Arguments.Add(new Variable(_token, 0));
                    _token = _lex.GetNextToken();
                }
                if (_lex.GetSym() == 99) //换行
                {
                    _token = _lex.GetNextToken();
                    while (_lex.GetSym() == 99) //换行
                    {
                        _token = _lex.GetNextToken();
                    }
                    func.CookingCommands = Commands();
                    if (_lex.GetSym() == 10) //ENDFUNCTION
                    {
                        _token = _lex.GetNextToken();
                        _count = 0;
                        if (_lex.GetSym() == 99) //换行
                        {
                            _token = _lex.GetNextToken();
                            while (_lex.GetSym() == 99) //换行
                            {
                                _token = _lex.GetNextToken();
                            }
                        }
                        else
                        {
                            _success = false;
                            Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
                        }
                        return func;
                    }
                    _success = false;
                    Console.Error.WriteLine("Error:Need a ENDFUNCTION... Line:{0}", _lex.GetLine());
                }
                else
                {
                    _success = false;
                    Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
                }
            }
            else
            {
                _success = false;
                Console.Error.WriteLine("Error:The function must be Named... Line:{0}", _lex.GetLine());
            }
            return null;
        }

        //主体部分
        private void MainPart()
        {
            if(_lex.GetSym()==3)//BEGIN
            {
                _token = _lex.GetNextToken();
                if (_lex.GetSym() == 99) //换行
                {
                    _token = _lex.GetNextToken();
                    while (_lex.GetSym() == 99) //换行
                    {
                        _token = _lex.GetNextToken();
                    }
                    _tokenTable.CookingCommands = Commands();
                    if (_lex.GetSym() == 4) //END
                    {
                        _count = 0;
                        _token = _lex.GetNextToken();
                        if (_lex.GetSym() == 99) //换行
                        {
                            _token = _lex.GetNextToken();
                            while (_lex.GetSym() == 99) //换行
                            {
                                _token = _lex.GetNextToken();
                            }
                        }
                        else
                        {
                            _success = false;
                            Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
                        }
                    }
                    else
                    {
                        _success = false;
                        Console.Error.WriteLine("Error:Need a END... Line:{0}", _lex.GetLine());
                    }
                }
                else
                {
                    _success = false;
                    Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
                }
            }
            else
            {
                _success = false;
                Console.Error.WriteLine("Error:Need a BEGIN... Line:{0}", _lex.GetLine());
            }
        }

        //命令序列
        private List<CookingCommand> Commands()
        {
            List<CookingCommand> cmds = new List<CookingCommand>();
            Command(ref cmds);
            while (_lex.GetSym() != 4 && _lex.GetSym() != 6 && _lex.GetSym() != 8 && _lex.GetSym() != 10) //END  ENDIF  ENDWHILE  ENDFUNCTION
            {
                Command(ref cmds);
            }
            return cmds;
        }

        //命令
        private void Command(ref List<CookingCommand> cmds)
        {
            switch (_lex.GetSym())
            {
                case 5: //WHILE
                    Loop(ref cmds);
                    break;
                case 7: //IF
                    Condition(ref cmds);
                    break;
                case 20: //标识符
                    cmds.Add(CommonCommand());
                    break;
                default:
                    _success = false;
                    Console.Error.WriteLine("Error:Should be a WHILE,or a IF, or a Correct Command... Line:{0}", _lex.GetLine());
                    break;
            }
        }

        //循环
        private void Loop(ref List<CookingCommand> cmds)
        {
            _token = _lex.GetNextToken();
            //循环的开头
            int bp = _count;
            Variable con = LogicalExpression(ref cmds);
            cmds.Add(new CookingCommand("#if", new List<Argument> {new Argument(con), new Argument(_count + 2)}));
            _count++;
            //需要回填的指令号
            int toBp = _count;
            cmds.Add(new CookingCommand("#goto", new List<Argument>()));
            _count++;
            if (_lex.GetSym() == 99) //换行
            {
                _token = _lex.GetNextToken();
                while (_lex.GetSym() == 99) //换行
                {
                    _token = _lex.GetNextToken();
                }
                List<CookingCommand> cmdsAdd = Commands();
                cmds.AddRange(cmdsAdd);
                _count += cmdsAdd.Count;
                if (_lex.GetSym() == 6) //ENDWHILE
                {
                    cmds.Add(new CookingCommand("#goto", new List<Argument> {new Argument(bp)}));
                    _count++;
                    //回填
                    cmds[toBp].Arguments.Add(new Argument(_count));
                    _token = _lex.GetNextToken();
                    if (_lex.GetSym() == 99) //换行
                    {
                        _token = _lex.GetNextToken();
                        while (_lex.GetSym() == 99) //换行
                        {
                            _token = _lex.GetNextToken();
                        }
                    }
                    else
                    {
                        _success = false;
                        Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
                    }
                }
                else
                {
                    _success = false;
                    Console.Error.WriteLine("Error:Should be a ENDWHILE... Line:{0}", _lex.GetLine());
                }
            }
            else
            {
                _success = false;
                Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
            }
        }

        //逻辑表达式
        //OR的处理思想是，如果所有参数均为0，则结果为0，否则为1。故参数可能有很多个，而不止一个或者两个；结果会保存在最后一个参数中
        private Variable LogicalExpression(ref List<CookingCommand> cmds)
        {
            List<Argument> args = new List<Argument>();
            args.Add(new Argument(LogicalItem(ref cmds)));
            while(_lex.GetSym()==12)//OR
            {
                _token = _lex.GetNextToken();
                args.Add(new Argument(LogicalItem(ref cmds)));
            }
            //存放运算结果的变量
            Variable res = new Variable("#T"+_temp++, 0);
            args.Add(new Argument(res));
            cmds.Add(new CookingCommand("OR",args));
            _count++;
            return res;
        }

        //逻辑项
        private Variable LogicalItem(ref List<CookingCommand> cmds)
        {
            List<Argument> args = new List<Argument>();
            args.Add(new Argument(LogicalFactor(ref cmds)));
            while(_lex.GetSym() == 11)//AND
            {
                _token = _lex.GetNextToken();
                args.Add(new Argument(LogicalFactor(ref cmds)));//TODO 得到的返回值可能为空，以上很多方法也都存在这个问题
            }
            Variable res = new Variable("#T"+_temp++, 0);
            args.Add(new Argument(res));
            cmds.Add(new CookingCommand("AND", args));
            _count++;
            return res;
        }

        //逻辑因子
        private Variable LogicalFactor(ref List<CookingCommand> cmds)
        {

            if (_lex.GetSym() == 13)//NOT
            {
                _token = _lex.GetNextToken();
                Variable ce = ConditionExpression(ref cmds);
                if (ce != null)
                {
                    Variable res = new Variable("#T" + _temp++, 0);
                    cmds.Add(new CookingCommand("NOT", new List<Argument> {new Argument(ce), new Argument(res)}));
                    _count++;
                    return res;
                }
                return null;
            }
            return ConditionExpression(ref cmds);
        }

        //条件表达式
        private Variable ConditionExpression(ref List<CookingCommand> cmds)
        {
            Variable exp1 = Expression(ref cmds);
            if (_lex.GetSym() >= 40 && _lex.GetSym() <= 45) //条件运算符
            {
                //运算符
                string op = _token;
                _token = _lex.GetNextToken();
                Variable exp2 = Expression(ref cmds);
                Variable res = new Variable("#T" + _temp++, 0);
                cmds.Add(new CookingCommand(op,
                                            new List<Argument>
                                                {new Argument(exp1), new Argument(exp2), new Argument(res)}));
                _count++;
                return res;
            }
            _success = false;
            Console.Error.WriteLine("Error:Should be a Condition Operator... Line:{0}", _lex.GetLine());
            return null;
        }

        //数值表达式
        private Variable Expression(ref List<CookingCommand> cmds)
        {
            Variable item1 = Item(ref cmds);
            while (_lex.GetSym() == 30 || _lex.GetSym() == 31) //+  -
            {
                string op = _token;
                _token = _lex.GetNextToken();
                Variable item2 = Item(ref cmds);
                Variable res = new Variable("#T" + _temp++,0);
                cmds.Add(new CookingCommand(op,
                                            new List<Argument>
                                                {new Argument(item1), new Argument(item2), new Argument(res)}));
                _count++;
                item1 = res;
            }
            return item1;
        }

        //数值项
        private Variable Item(ref List<CookingCommand> cmds)
        {
            Variable fac1 = Factor(ref cmds);
            while (_lex.GetSym() == 32 || _lex.GetSym() == 33)//*  /
            {
                string op = _token;
                _token = _lex.GetNextToken();
                Variable fac2 = Factor(ref cmds);
                Variable res = new Variable("#T" + _temp++, 0);
                cmds.Add(new CookingCommand(op,
                                            new List<Argument> { new Argument(fac1), new Argument(fac2), new Argument(res) }));
                _count++;
                fac1 = res;
            }
            return fac1;
        }

        //数值因子
        private Variable Factor(ref List<CookingCommand> cmds)
        {
            switch (_lex.GetSym())
            {
                case 20: //标识符
                    Variable resI = new Variable(_token, 0); //TODO 这个会埋下隐患，执行的时候要注意处理，获取其真正的值
                    _token = _lex.GetNextToken();
                    return resI;
                case 21: //数值
                    Variable resN = new Variable("#T" + _temp++, double.Parse(_token));
                    _token = _lex.GetNextToken();
                    return resN;
                case 50: //(
                    Variable resE = Expression(ref cmds);
                    if (_lex.GetSym() == 51) //)
                    {
                        _token = _lex.GetNextToken();
                        return resE;
                    }
                    _success = false;
                    Console.Error.WriteLine("Error:Need a \")\"... Line:{0}", _lex.GetLine());
                    break;
                default:
                    _success = false;
                    Console.Error.WriteLine("Error:Should be a Identifier,or a Number,or a Expression... Line:{0}",
                                            _lex.GetLine());
                    break;
            }
            return null;//TODO 处理null可能引发的异常
        }

        //分支
        private void Condition(ref List<CookingCommand> cmds)
        {
            _token = _lex.GetNextToken();
            Variable con = LogicalExpression(ref cmds);
            cmds.Add(new CookingCommand("#if", new List<Argument> {new Argument(con), new Argument(_count + 2)}));
            _count++;
            //需要回填的指令号
            int toBp = _count;
            cmds.Add(new CookingCommand("#goto", new List<Argument>()));
            _count++;
            if (_lex.GetSym() == 99) //换行
            {
                _token = _lex.GetNextToken();
                while (_lex.GetSym() == 99) //换行
                {
                    _token = _lex.GetNextToken();
                }
                List<CookingCommand> cmdsAdd = Commands();
                cmds.AddRange(cmdsAdd);
                _count += cmdsAdd.Count;
                if (_lex.GetSym() == 8) //ENDIF
                {
                    //回填
                    cmds[toBp].Arguments.Add(new Argument(_count));
                    _token = _lex.GetNextToken();
                    if (_lex.GetSym() == 99) //换行
                    {
                        _token = _lex.GetNextToken();
                        while (_lex.GetSym() == 99) //换行
                        {
                            _token = _lex.GetNextToken();
                        }
                    }
                    else
                    {
                        _success = false;
                        Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
                    }
                }
                else
                {
                    _success = false;
                    Console.Error.WriteLine("Error:Should be a ENDIF... Line:{0}", _lex.GetLine());
                }
            }
            else
            {
                _success = false;
                Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
            }
        }

        //普通命令
        private CookingCommand CommonCommand()
        {
            string name = _token;
            List<Argument> args = new List<Argument>();
            _token = _lex.GetNextToken();
            while(_lex.GetSym() ==20 || _lex.GetSym()==21)//标识符、数值
            {
                args.Add(_lex.GetSym() == 20
                             ? new Argument(new Variable(_token, 0))
                             : new Argument(double.Parse(_token)));
                _token = _lex.GetNextToken();
            }
            if (_lex.GetSym() == 99) //换行
            {
                _token = _lex.GetNextToken();
                while (_lex.GetSym() == 99) //换行
                {
                    _token = _lex.GetNextToken();
                }
            }
            else
            {
                _success = false;
                Console.Error.WriteLine("Error:Need a Line Feed... Line:{0}", _lex.GetLine());
            }
            return new CookingCommand(name, args);
        }
    }
}
